#include <nxos.h>

#define PRODUCT_NR 5
#define WAIT_TIMES 15
NX_PRIVATE int productNumber;

NX_PRIVATE NX_Solt mutex;
NX_PRIVATE NX_Solt condPush;
NX_PRIVATE NX_Solt condPop;

NX_U32 ConsumerEntry1(void *arg)
{
    for (int i = 0 ; i < WAIT_TIMES ; ++i)
    {
        NX_MutexAcquire(mutex);
        if (productNumber == 0) /* no product, wait */
        {
            NX_ConditionWait(condPush, mutex);
        }
        productNumber--;
        NX_Printf("consumer-%d: the productNumber is %d now\n", NX_ThreadGetCurrentId(), productNumber);
        NX_ConditionSignal(condPop);
        NX_MutexRelease(mutex);
    }
    return 0;
}

NX_U32 ProducerEntry1(void *arg)
{
    
    for (int i = 0 ; i < WAIT_TIMES ; ++i)
    {
        NX_MutexAcquire(mutex);
        if(productNumber == PRODUCT_NR){ /* product full, wait */
            NX_ConditionWait(condPop, mutex);
        }
        productNumber++;
        NX_Printf("producer-%d: the productNumber is %d now\n",NX_ThreadGetCurrentId(),productNumber);
        NX_ConditionSignal(condPush);
        NX_MutexRelease(mutex);
    }
    return 0;
}

void ProducerConsumerTest1(void)
{
    NX_Printf("\nProducerConsumerTest1 start\n");
    
    mutex = NX_MutexCreate(0);//初始化互斥量
    condPush = NX_ConditionCreate(0);//初始化条件变量
    condPop = NX_ConditionCreate(0);

    if (mutex == NX_SOLT_INVALID_VALUE || condPush == NX_SOLT_INVALID_VALUE || condPop == NX_SOLT_INVALID_VALUE)
    {
        return;
    }

    productNumber = 0;

    NX_Solt thread1;
    NX_Solt thread2;
    thread1 = NX_ThreadCreate(NX_NULL, ConsumerEntry1, NX_NULL, 0);
    thread2 = NX_ThreadCreate(NX_NULL, ProducerEntry1, NX_NULL, 0);

    if (thread1 == NX_SOLT_INVALID_VALUE || thread2 == NX_SOLT_INVALID_VALUE)
    {
        NX_MutexDestroy(mutex);
        NX_ConditionDestroy(condPush);
        NX_ConditionDestroy(condPop);
        return;
    }

    NX_ThreadWait(thread1, NX_NULL);
    NX_ThreadWait(thread2, NX_NULL);

    NX_Printf("\nthe productNumber finally is %d\n",productNumber);

    NX_MutexDestroy(mutex);
    NX_ConditionDestroy(condPush);
    NX_ConditionDestroy(condPop);
}

NX_U32 ConsumerEntry2(void *arg)
{    
    for (int i = 0 ; i < WAIT_TIMES ; ++i)
    {
        NX_MutexAcquire(mutex);
        while (productNumber == 0){
            NX_ConditionWait(condPush, mutex);
        }
        productNumber--;
        NX_Printf("consumer-%d: the productNumber is %d now\n", NX_ThreadGetCurrentId(), productNumber);
        NX_ConditionSignal(condPop);
        NX_MutexRelease(mutex);
    }
    return 0;
}

NX_U32 ProducerEntry2(void*arg)
{
    for (int i = 0 ; i < WAIT_TIMES ; ++i)
    {
        NX_MutexAcquire(mutex);
        while (productNumber == PRODUCT_NR)
        {
            NX_ConditionWait(condPop, mutex);
        }
        productNumber++;
        NX_Printf("producer-%d: the productNumber is %d no\nw", NX_ThreadGetCurrentId(), productNumber);
        NX_ConditionSignal(condPush);
        NX_MutexRelease(mutex);
    }
    return 0;
}

void ProducerConsumerTest2(void)
{
    NX_Printf("\nProducerConsumerTest2 start\n");
    
    mutex = NX_MutexCreate(0);//初始化互斥量
    condPush = NX_ConditionCreate(0);//初始化条件变量
    condPop = NX_ConditionCreate(0);

    productNumber = 0;
    
    NX_Solt producerThread[10];
    NX_Solt consumerThread[10];
    
    for(int i = 0 ; i < 10 ; ++i){//创建10个消费者线程
        consumerThread[i] = NX_ThreadCreate(NX_NULL, ConsumerEntry2, NX_NULL, 0);
        if (consumerThread[i] == NX_SOLT_INVALID_VALUE)
            NX_Printf("consumer thread %d create failed!\n", i);
        else
            NX_Printf("consumer thread %d create ok!\n", i);
    }
    for(int i = 0 ; i < 10 ; ++i){//创建10个生产者线程
        producerThread[i] = NX_ThreadCreate(NX_NULL, ProducerEntry2, NX_NULL, 0);
        if (producerThread[i] == NX_SOLT_INVALID_VALUE)
            NX_Printf("producer thread %d create failed!\n", i);
        else
            NX_Printf("producer thread %d create ok!\n", i);
    }

    for(int i = 0 ; i < 10 ; ++i){//创建20个生产者线程
        if (consumerThread[i] != NX_SOLT_INVALID_VALUE)
            NX_ThreadWait(consumerThread[i], NX_NULL);
        if (producerThread[i] != NX_SOLT_INVALID_VALUE)
            NX_ThreadWait(producerThread[i], NX_NULL);
    }

    NX_Printf("\nthe productNumber finally is %d\n", productNumber);
    
    NX_MutexDestroy(mutex);
    NX_ConditionDestroy(condPush);
    NX_ConditionDestroy(condPop);

}

NX_Error TEST_Condition(char *cmdline)
{
    ProducerConsumerTest1();
    NX_ThreadSleep(100);
    ProducerConsumerTest2();
    return NX_EOK;
}
